﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Reflection;

namespace GameEditor.Common
{


    /// <summary>
    /// UIBridge提供了一个让C++能直接使用C#定义的UI的办法。
    /// </summary>
    public class UIBridge : IDisposable
    {

        private Form internalForm;
        private Dictionary<string, MethodInfo> notifyMethods = new Dictionary<string, MethodInfo>();


        public Form InternalForm
        {
            get { return internalForm; }
        }

        protected UIBridge()
        {
            //创建窗口
            BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
            Type type = GetType();
            RemoteFormAttribute remoteForm = (RemoteFormAttribute)Attribute.GetCustomAttribute(type, typeof(RemoteFormAttribute));

            Type formType = null;
            if (string.IsNullOrEmpty(remoteForm.Assembly))
            {
                formType = Type.GetType(remoteForm.FormType);
            }
            else
            {
                Assembly assembly = Assembly.LoadFrom(remoteForm.Assembly);
                formType = assembly.GetType(remoteForm.FormType);
            }

            if (formType == null)
                throw new Exception("找不到类 "+remoteForm.FormType);


            internalForm = (Form)Activator.CreateInstance(formType);
            //关联字段引用
            FieldInfo[] fields = type.GetFields(flags);
            
            foreach (FieldInfo field in fields)
            {
                RemoteReferenceAttribute reference = Attribute.GetCustomAttribute(field, typeof(RemoteReferenceAttribute)) as RemoteReferenceAttribute;
                if (reference == null)
                    continue;
                //然后查找对应的名字
                string referenceName = reference.ReferenceName;
                if (string.IsNullOrEmpty(referenceName))
                    referenceName = field.Name;


                FieldInfo targetField = formType.GetField(referenceName, flags);
                if (targetField == null)
                    throw new Exception("目标对象中没有找到可被绑定的字段" + referenceName);
                //将字段值复制过去
                object value = targetField.GetValue(internalForm);
                field.SetValue(this, value);
                //fieldMapping.Add(referenceName, value);
            }
            //关联事件引用
            Dictionary<string, object> fieldMapping = new Dictionary<string, object>();
            MethodInfo[] methods = type.GetMethods(flags);
            foreach (MethodInfo method in methods)
            {
                RemoteEventAttribute reference = Attribute.GetCustomAttribute(method, typeof(RemoteEventAttribute)) as RemoteEventAttribute;
                if (reference != null)
                {
                    //然后查找对应的名字
                    object field = null;
                    if (fieldMapping.ContainsKey(reference.ControlName))
                    {
                        field = fieldMapping[reference.ControlName];
                    }
                    else
                    {
                        //没有引用过该对象，则在窗口内查找
                        FieldInfo fieldInfo = formType.GetField(reference.ControlName, flags);
                        field = fieldInfo.GetValue(internalForm);
                        if (field != null)
                            fieldMapping.Add(reference.ControlName, field);
                    }
                    if (field == null)
                        throw new Exception(String.Format("在类{0}中找不到使用RemoteReference标记的成员{1}！", type.FullName, reference.ControlName));

                    //然后查找事件
                    EventInfo eventInfo = field.GetType().GetEvent(reference.EventName, flags);
                    System.Delegate handler = System.Delegate.CreateDelegate(eventInfo.EventHandlerType, this, method);
                    //添加到事件上
                    eventInfo.AddEventHandler(field, handler);
                }

                //记录通知方法
                RemoteNotifyAttribute notify = Attribute.GetCustomAttribute(method, typeof(RemoteNotifyAttribute)) as RemoteNotifyAttribute;
                if (notify != null)
                {
                    string name = notify.CallbackName;
                    if (string.IsNullOrEmpty(name))
                        name = method.Name;
                    notifyMethods.Add(name, method);
                }
            }

            //关联目标对象内部的UIBridge类型的对象
            foreach (FieldInfo field in formType.GetFields(flags | BindingFlags.DeclaredOnly))
            {
                if (field.FieldType == typeof(UIBridge))
                    field.SetValue(internalForm, this);
            }

            //关联事件通知
            

            OnInitialize();

        }

        protected virtual void OnInitialize()
        {
        }


        /// <summary>
        /// 将参数转发给当前函数的调用者对应的函数上
        /// </summary>
        /// <param name="args"></param>
        public void NotifyEvent(params object[] args)
        {
            //提取调用者的名字
            System.Diagnostics.StackTrace traces = new System.Diagnostics.StackTrace();
            string methodName = traces.GetFrame(1).GetMethod().Name;
            //查询通知方法
            if (notifyMethods.ContainsKey(methodName))
            {
                //调用方法
                MethodInfo method = notifyMethods[methodName];
                if (args == null)
                    args = new object[0];
                method.Invoke(this, args);
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (internalForm != null && !internalForm.IsDisposed)
            {
                internalForm.Dispose();
                internalForm = null;
            }

        }

        #endregion
    }
    /// <summary>
    /// UIBridge的子类必须添加该Attribute以标出使用哪个外部的窗体
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class RemoteFormAttribute : Attribute
    {
        private String formType;
        private String assembly;
        public String Assembly
        {
            get { return assembly; }
        }
        public String FormType
        {
            get { return formType; }
        }

        /// <summary>
        /// 查找当前程序集内的类型
        /// </summary>
        /// <param name="formType"></param>
        public RemoteFormAttribute(String formType)
        {
            this.formType = formType;
            assembly = null;
        }
        /// <summary>
        /// 查找外部程序集的类型
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="formType"></param>
        public RemoteFormAttribute(String assembly, String formType)
        {
            this.formType = formType;
            this.assembly = assembly;
        }
    }

    /// <summary>
    /// 由该属性描述的字段，会自动关联到窗口类中的对应字段。
    /// </summary>
    [AttributeUsage(AttributeTargets.Field, AllowMultiple=false)]
    public class RemoteReferenceAttribute : Attribute
    {
        private string referenceName;
        public String ReferenceName
        {
            get { return referenceName; }
        }
        /// <summary>
        /// 在没有指定引用名的时候，则自动关联到同名的字段上
        /// </summary>
        public RemoteReferenceAttribute()
        {
        }
        /// <summary>
        /// 关联到指定名字的字段上
        /// </summary>
        /// <param name="referenceName"></param>
        public RemoteReferenceAttribute(string referenceName)
        {
            this.referenceName = referenceName;
        }
    }

    /// <summary>
    /// 该属性用于描述UIBridge子类中的方法
    /// 该属性描述的方法会自动将该方法绑定到指定控件的指定事件上
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class RemoteEventAttribute : Attribute
    {
        private string controlName;
        private string eventName;

        public string ControlName
        {
            get { return controlName; }
        }
        public string EventName
        {
            get { return eventName; }
        }
        /// <summary>
        /// 将当前方法注册到指定控件的指定事件上
        /// </summary>
        /// <param name="controlName">需要关联到哪个控件上</param>
        /// <param name="eventName">关联该控件的事件名</param>
        public RemoteEventAttribute(string controlName, string eventName)
        {
            this.controlName = controlName;
            this.eventName = eventName;
        } 
    }


    
    
    /// <summary>
    /// 使用该属性修饰的方法，可以在窗口类里通过调用UIBridge的NotifyEvent方法来转发。
    /// 
    /// 
    /// 比如在UIBridge子类中：
    /// [RemoteNotify]
    /// private void button1_Click(object sender, EventArgs e)
    /// {
    ///     //...
    /// }
    /// 在窗口类中
    /// private UIBridge bridge;
    /// 
    /// private void button1_Click(object sender, EventArgs e)
    /// {
    ///     bridge.NotifyEvent(sender, e);
    /// }
    /// 则在窗口类的button1_Click被调用时同时调用UIBridge子类里的button1_Click
    /// 这个方法比使用RemoteEvent方便，但是效率远低于RemoteEvent，不适合频繁发生的事件里
    /// 优点是不用关心UIBridge子类中的方法是绑定到哪个事件上，用起来比较简单。
    /// 关于窗口类中的bridge初始化问题，设置为null即可，UIBridge会自动设置该字段。
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class RemoteNotifyAttribute : Attribute
    {
        private string callbackName;
        public string CallbackName
        {
            get { return callbackName; }
        }
        /// <summary>
        /// 将当前方法关联到指定窗口类的事件回调方法上
        /// </summary>
        /// <param name="callbackName"></param>
        public RemoteNotifyAttribute(string callbackName)
        {
            this.callbackName = callbackName;
        }
        /// <summary>
        /// 将当前方法关联到窗口类的同名的事件回调方法上
        /// </summary>
        /// <param name="callbackName"></param>
        public RemoteNotifyAttribute()
        {
        }
    }



}
